package android.db;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import android.app.gfestival.soap.ArtistDescription;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.util.Log;


/**
 * 
 * @author son.dt
 *
 */
public abstract class AbsDAO<T extends Persistence> implements DAO<T>{
	private Context context;
	private String tableName;
	private Class<T> type;
	
	public AbsDAO(Context context){
		this.context = context;
		//get class type of <T>
		type = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
		Table tblAnn = type.getAnnotation(Table.class);
		tableName = tblAnn.name();
	}
	
	public String createTableCommand(){
		Field[] fields = type.getFields();
		String command = "CREATE TABLE IF NOT EXISTS " + getTableName() + " (";
		for(int i = 0; i < fields.length; i++){
			Column column = fields[i].getAnnotation(Column.class);
			if(column!= null) command = command + column.name() + " " + column.type() + ",";
		}
		command = command.substring(0, command.length() - 1);
		command = command + ");";
		return command;
	}

	public String getTableName(){
		return tableName;
	}
	
	@Override
	public int deleteAll() {
		return delete(null, null);
	}
	
	@Override
	public int delete(Map<String, String> selections) {
		// TODO Auto-generated method stub
		if(selections.size() == 0) return 0;
		Set<String> key = selections.keySet();
		String[] selectionArgs = selections.values().toArray(new String[]{});
		String[] selecColumn = key.toArray(new String[]{});
		String selection = "";
		for(String col : selecColumn){
			selection = selection + "and " + col + "=? ";
		}
		selection = selection.substring("and ".length(), selection.length());
		return delete(selection, selectionArgs);
	}
	public long insert(T obj) throws Exception {
		Field[] fields = obj.getClass().getFields();
		ContentValues values = new ContentValues();
		for(int i = 0; i < fields.length; i++){
			Column column = fields[i].getAnnotation(Column.class);
			if(column != null)
				values.put(column.name(), (String) fields[i].get(obj));
		}
		return insert(null, values);
	};
	
	@Override
	public ArrayList<T> findByParam(Map<String, String> selections) throws Exception {
		Set<String> key = selections.keySet();
		String[] selectionArgs = selections.values().toArray(new String[]{});
		String[] selecColumn = key.toArray(new String[]{});
		String selection = "";
		for(String col : selecColumn){
			selection = selection + "and " + col + "=? ";
		}
		selection = selection.substring("and ".length(), selection.length());
		Cursor cursor = query(null, selection, selectionArgs, null, null, null);
		
		return cursorToArray(cursor);
	}
	@Override
	public ArrayList<T> cursorToArray(Cursor cursor) throws Exception{
		ArrayList<T> list = new ArrayList<T>();
		if(cursor.getCount() > 0){
			cursor.moveToFirst();
			do{
				T obj = type.newInstance();
				Field[] fields = obj.getClass().getFields();
				for(int i = 0; i < fields.length; i++){
					Column column = fields[i].getAnnotation(Column.class);
					if(column != null){
						fields[i].setAccessible(true);
						fields[i].set(obj, getColumnValue(column, cursor));
					}
				}
				list.add(obj);
			}while(cursor.moveToNext());
		}
		cursor.close();
		return list;
	}
	
	@Override
	public ArrayList<T> findAll() throws Exception {
		Cursor cursor = query(null, null, null, null, null, null);
		return cursorToArray(cursor);
	}
	
	public Cursor findAllReturnCursor() throws Exception{
		return query(null, null, null, null, null, null);
	}
	public Cursor findAllReturnCursor(String orderBy) throws Exception{
		return query(null, null, null, null, null, orderBy);
	}
	
	public int insertList(List<T> items) throws Exception{
		int eff = 0;
		for(int i = 0; i < items.size(); i++){
			insert(items.get(i));
			eff++;
		}
		return eff;
	}
	
	private Object getColumnValue(Column col, Cursor cursor){
		if(col.type().equalsIgnoreCase("TEXT")){
			return cursor.getString(cursor.getColumnIndex(col.name()));
		}else if(col.type().equalsIgnoreCase("INTEGER")){
			return cursor.getInt(cursor.getColumnIndex(col.name()));
		}else if(col.type().equalsIgnoreCase("FLOAT")){
			return cursor.getFloat(cursor.getColumnIndex(col.name()));
		}else if(col.type().equalsIgnoreCase("LONG")){
			return cursor.getLong(cursor.getColumnIndex(col.name()));
		}else if(col.type().equalsIgnoreCase("DOUBLE")){
			return cursor.getDouble(cursor.getColumnIndex(col.name()));
		}else if(col.type().equalsIgnoreCase("BLOB")){
			return cursor.getInt(cursor.getColumnIndex(col.name()));
		}
		return null;
	}
	
	public int updateByParam(T obj, java.util.Map<String,String> wheres) throws Exception {
		Field[] fields = obj.getClass().getFields();
		Set<String> key = wheres.keySet();
		String[] whereArgs = wheres.values().toArray(new String[]{});
		String[] whereColumn = key.toArray(new String[]{});
		String whereClause = "";
		for(String col : whereColumn){
			whereClause = whereClause + "and " + col + "=? ";
		}
		whereClause = whereClause.substring("and ".length(), whereClause.length());
		ContentValues values = new ContentValues();
		for(int i = 0; i < fields.length; i++){
			Column column = fields[i].getAnnotation(Column.class);
			if(column != null){
				if(!key.contains(column.name()))
					values.put(column.name(), (String) fields[i].get(obj));
			}
		}
		return update(values, whereClause, whereArgs);
	};
	
	protected Context getContext(){
		return context;
	}
	
	protected abstract Cursor query( String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy);
	protected abstract int update (ContentValues values, String whereClause, String[] whereArgs);
	protected abstract int delete (String whereClause, String[] whereArgs);
	protected abstract long insert (String nullColumnHack, ContentValues values);
	protected abstract void execSQL(String sql);
}
